#pragma once

#ifdef _DEBUG
#include <iostream>
using namespace std;
#endif

#include "Vector3.h"
#include "TriVertex.h"
#include "Quaternion.h"
#include <set>

using namespace Math;
//////////////////////////////////////////////////////////////////////////////
// Forward Declarations
//////////////////////////////////////////////////////////////////////////////
class TriEdge;
//class TriVertex;
class TriFaceRender;
class Tetrahedron;


//////////////////////////////////////////////////////////////////////////////
// Types
//////////////////////////////////////////////////////////////////////////////

struct Segment
{
   pair<Vector3,Vector3> coords;
   Vector3 hole;
   int seeds;
};

class Comp
{
public:
   bool operator()(Segment s1, Segment s2)
   {
      if(s1.seeds < s2.seeds )
         return true;
      else
         return false;
   }
};


//////////////////////////////////////////////////////////////////////////////
// TriFace
//
// Description:
//      Triangle class for the triangular mesh.
//////////////////////////////////////////////////////////////////////////////
class TriFace
{
public:
   typedef enum {NONE=0, VERTEX, EDGE} Degenerate;

private:
   int id;

   bool marked;

   // Geometry info
   Vector3 normal;
   double d;
   Real area;

   Vector3 color;

   // TriEdge/Topology info
   TriEdge* e1;
   TriEdge* e2;
   TriEdge* e3;
   Degenerate degenerate;

   // Texture Info:
   Vector2 _uv0;
   Vector2 _uv1;
   Vector2 _uv2;
   

   vector<Tetrahedron*> myTetrahedrons;
   
   multiset<Segment,Comp> segments;
   
public:
   TriFace(void) : e1(NULL), e2(NULL), e3(NULL) 
   {}

   ~TriFace(void);

   void Initialize(void);

    void Copy(TriFace* f) 
    {
        SetId(f->Id());
        if (f->Marked()) Mark();

        SetNormal(f->Normal());
       SetDistance(f->Distance());

        SetEdge(0, f->Edge1());
        SetEdge(1, f->Edge2());
        SetEdge(2, f->Edge3());

        SetDegenerateMode(f->DegenerateMode());

        this->_uv0.set(f->uv0());
        this->_uv1.set(f->uv1());
        this->_uv2.set(f->uv2());
    }

    //Texture coordinates method.
	const Vector2 & uv0() const {
		return this->_uv0;
	}

	const Vector2 & uv1() const {
		return this->_uv1;
	}

	const Vector2 & uv2() const {
		return this->_uv2;
	}

	void uv0(Vector2 & v) {
		this->_uv0.set(v[0], v[1]);
	}

	void uv1(Vector2 & v) {
		this->_uv1.set(v[0], v[1]);
	}

	void uv2(Vector2 & v) {
		this->_uv2.set(v[0], v[1]);
	}

    void uv0(Real u, Real v) {
        this->_uv0.x(u);
        this->_uv0.y(v);
	}

    void uv1(Real u, Real v) {
        this->_uv1.x(u);
        this->_uv1.y(v);
	}

    void uv2(Real u, Real v) {
        this->_uv2.x(u);
        this->_uv2.y(v);
	}

   // Get functions
   int Id(void) {
      return id;
   }

   const Vector3& Normal(void) const {
      return normal;
   }

   double Distance(void) const {
      return d;
   }

   int VertexId(TriVertex *v) const
   {
      //This method returns the vertex id for this face, not for the mesh!!
      if(v==Vertex1()) return 0;
      else if(v==Vertex2()) return 1;
      else if(v==Vertex3()) return 2;
      else
      {
#ifdef _DEBUG
         cerr << "Error: Bad vertex passed to TriFace: VertexId" << endl;
#endif
         return -1;
      }
   }

   TriVertex* Vertex1(void) const;

   TriVertex* Vertex2(void) const;

   TriVertex* Vertex3(void) const;

   TriVertex* Vertex(int id) const
   {
      //This method returns the vertex with id in this face
      switch(id)
      {
      case 0:
         return Vertex1();
      case 1:
         return Vertex2();
      case 2:
         return Vertex3();
      default:
#ifdef _DEBUG
         cerr << "Error: Bad vertex passed to TriFace: Vertex" << endl;
#endif
         return NULL;
      }
   }

   bool Contains(const TriVertex* v) const;

   TriEdge* Edge1(void) const
   {
      return e1;
   }

   TriEdge* Edge2(void) const
   {
      return e2;
   }

   TriEdge* Edge3(void) const
   {
      return e3;
   }

   int Edge_Id(const TriEdge* e) const
   {
      //This method returns the edge id for this face, not for the mesh!!
      if(e==e1) return 0;
      else if(e==e2) return 1;
      else if(e==e3) return 2;
      else
      {
#ifdef _DEBUG
         cerr << "Error: Bad edge passed to TriFace: Edge_Id" << endl;
#endif
         return -1;
      }
   }

   /// Computes and returns the area.
   ///
   void computeArea();
   Real getArea() const
   {
      return area;
   }

   Vector3& Color(void)
   {
      return color;
   }

   /// Computes the centre of this triangle.
   ///
   Vector3 getCentroid();

   TriEdge* EdgeP(int i) const
   {
      switch(i)
      {
      case 0: return e1;
      case 1: return e2;
      case 2: return e3;
      default:
#ifdef _DEBUG
         cerr << "Error: Bad index passed to TriFace: EdgeP" << endl;
#endif
         return NULL;
      }
   }

   TriEdge* EdgeP(TriVertex* v) const
   {
      if(v==Vertex1())
      {
         return e1;
      }
      else if(v==Vertex2())
      {
         return e2;
      }
      else if(v==Vertex3())
      {
         return e3;
      }
      else
      {
#ifdef _DEBUG
         cerr << "Error: Bad vertex passed to TriFace::EdgeP" << endl;
#endif
         return NULL;
      }
   }

   TriEdge* EdgeP(TriFace* f) const;

   Degenerate DegenerateMode(void) const
   {
      return degenerate;
   }

   // Set functions
    void SetId(int i)
   {
      id=i;
   }

    void SetDistance(double distance)
    {
        d = distance;
    }

    void SetDegenerateMode(Degenerate d)
    {
        degenerate = d;
    }


    void SetEdge(int i, TriEdge* e)
    {
        if (i==0) e1 = e;
        if (i==1) e2 = e;
        if (i==2) e3 = e;
    }

   void ResetInternalEdgePointers(void);

   void CheckDegeneracies(void);

   // Geometric set functions
   void SetNormal(const Vector3& n)
   {
      normal=n;
   }

   void SetNormal(Real x, Real y, Real z)
   {
      normal=Vector3(x, y, z);
      normal.normalize();
   }

   // Boolean flag values for the face
   bool Marked(void) const
   {
      return marked;
   }

   // Set the boolean flags
   void Mark(void)
   {
      marked=true;
   }

   void Unmark(void)
   {
      marked=false;
   }

#ifdef _DEBUG
   bool VerifyTopology(int pos) const;

   bool VerifyGeometry(int pos) const;
#endif

   // Geometry functions
   void ComputeGeometry(void);

   void ComputeGeometryTwins(void);

   void ComputePlaneFromEdges(void);

   void ComputeNormal(void);
   void ComputeNormalInit(void);

    Vector3 GetBarycentricCoords(const Vector3 &p);

    bool IsPointInsideFace(const Vector3 &p);

    void GetPointProj(Vector3 &p, Vector3& proj, float &d);

   //OpenGL related functions
   void DrawPoints(void) const;
   void Draw(TriFaceRender *properties) const;
   bool IsGrasped(void) const;

   //Interpolation from deformation field
   bool IsFixed(void) const;

   //Collision detection
   Real MaxCollisionTime(void) const;

   bool operator< (const TriFace &other){
      return getCentroid() < ((TriFace)other).getCentroid();
   }

   void CalculateRotationMatrix2D(void)
   {      
      Quaternion q;
      Vector3 normal = Vector3::crossProd(Vertex2()->Position() - Vertex1()->Position(), Vertex3()->Position() - Vertex1()->Position());
      normal.normalize();          

   }

   vector<Tetrahedron*> &GetTetrahedrons() { return myTetrahedrons; }
      
   multiset<Segment,Comp> &GetSegments() { return segments; }

   
   
};


struct compareFace {

    bool operator()(TriFace *arg1, TriFace* arg2) const
    { return arg1->getCentroid() < arg2->getCentroid();}
};



